#pragma clang diagnostic push
#pragma ide diagnostic ignored "modernize-avoid-bind"

#include <future>
#include <utility>
#include "thread_manage.h"
#include "spdlog/spdlog.h"

namespace simpleThread {
    ThreadManage::ThreadManage(): ThreadManage(0, 0) {}

    ThreadManage::ThreadManage(int coreSize, int maxSize) : coreSize(coreSize), maxSize(maxSize) {
        if (coreSize <= 0) {
            this->coreSize = (int) std::thread::hardware_concurrency();
        }
        if (maxSize <= 0 || maxSize < coreSize) {
            this->maxSize = (int) std::thread::hardware_concurrency();
        }
        spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l pid:%P tid:%t %v");
        spdlog::info("New threadPool, coreSize:{}, maxSize:{}", this->coreSize,  this->maxSize);
    }

    ThreadManage::~ThreadManage() {
        this->shutdown();
        this->destroyThread();
        spdlog::info("close threadPool");
    }

    void ThreadManage::submit(const std::function<void()> &task) {
        if (!this->close) {
            this->isNewThread();
            this->taskQueue.push(task);
        }
    }

    void ThreadManage::isNewThread() {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        if (this->getActivateThreadList().size() < this->maxSize) {
            this->newThread();
        }
    }

    void ThreadManage::initThreads() {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        if (this->getActivateThreadList().size() < this->maxSize) {
            for (int i = 0; i < this->getCoreSize(); ++i) {
                this->newThread();
            }
        }
    }

    void ThreadManage::newThread() {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        auto *thread = new STLThread(&taskQueue);
        thread->setChecker(std::bind(&ThreadManage::threadTimeout, this, std::placeholders::_1));
        thread->setExitCall(std::bind(&ThreadManage::removeThread, this, std::placeholders::_1));
        thread->start();
        this->workThreads.insert(std::make_pair(thread->getId(), thread));
    }

    bool ThreadManage::removeThread(STLThread &thread) {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        this->destroyThread();
        if (thread.getState() == ThreadState::close) {
            // 从工作线程组中移除
            this->workThreads.erase(thread.getId());
            // 加入待回收线程组
            this->freeThreads.push_back(&thread);
            return true;
        }
        return false;
    }

    bool ThreadManage::threadTimeout(simpleThread::STLThread &thread) {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        time_t waitTime = std::time(nullptr) - thread.getExecuteTime();
        if (this->getActivateThreadList().size() > coreSize && waitTime > this->idleTime) {
            thread.timeout();
            return true;
        }
        return false;
    }

    void ThreadManage::destroyThread() {
        std::lock_guard<std::recursive_mutex> lock(this->mtx);
        for (auto t: this->freeThreads) {
            delete t;
        }
        this->freeThreads.clear();
    }

    void ThreadManage::join() {
        this->close = true;
        this->taskQueue.close();
        for (const auto &thread: this->getThreadList()) {
            thread->join();
        }
    }

    void ThreadManage::shutdown() {
        this->close = true;
        this->taskQueue.close();
        for (const auto &thread: this->getThreadList()) {
            thread->shutdown();
        }
    }

    std::vector<STLThread *> ThreadManage::getThreadList() {
        std::vector<STLThread *> threadList;
        threadList.reserve(this->workThreads.size());
        for (const auto &item: this->workThreads) {
            threadList.push_back(item.second);
        }
        return threadList;
    }

    std::vector<STLThread *> ThreadManage::getActivateThreadList() {
        std::vector<STLThread *> activateThreadList;
        activateThreadList.reserve(this->workThreads.size());
        for (const auto &item: this->workThreads) {
            if (item.second->getState() == ThreadState::activate) {
                activateThreadList.push_back(item.second);
            }
        }
        return activateThreadList;
    }

    void ThreadManage::setIdleTime(int n) {
        this->idleTime = n;
    }

    bool ThreadManage::isClose() const {
        return this->close;
    }

    int ThreadManage::getCoreSize() const {
        return coreSize;
    }

    int ThreadManage::getMaxSize() const {
        return maxSize;
    }

    int ThreadManage::getTotal() {
        return (int) this->workThreads.size();
    }
}

#pragma clang diagnostic pop